package com.dys.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * @className: com.dys.util.DatabaseUtil
 * @description: 封装工具类-封装获取连接&释放资源操作-跨平台方案
 * @author: pine cone
 * @version: v1.0.0
 * @createTime: 2024/09/12 下午8:41
 */
public class DatabaseUtils {
    /**
     * 这是一个main()方法,是应用程序的入口
     *
     * @param args 方法的形参列表,是一个字符串数组,包含了从命令行传递给程序的参数
     */
    public static void main(String[] args) {
        DatabaseUtils db = new DatabaseUtils();
    }

    private static final Properties PROP = new Properties();

    /**
     * 采用ThreadLocal的方式，ThreadLocal可以将Connection对象在Service层获取，并且存储到ThreadLocal中，
     * 当Dao层需要和数据库交互时，优先从ThreadLocal中尝试获取Connection对象。
     */
    private static final ThreadLocal<Connection> tl = new ThreadLocal<>();

    // 1.注册驱动
    static {
        try {
            // 优先通过PROP对象，加载database.properties文件
            InputStream is = DatabaseUtils.class.getResourceAsStream("/database.properties");
            PROP.load(is);
            System.out.println("配置文件：" + PROP.getProperty("jdbc.driver"));
            Class.forName(PROP.getProperty("jdbc.driver"));
        } catch (ClassNotFoundException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 2.与MySQL建立连接,获取连接对象-->解决不是同一个Connection对象的问题
     *
     * @return 连接对象
     */
    public static Connection getConnection() {
        // 1、先尝试从ThreadLocal中获取。
        Connection conn = tl.get();
        if (conn == null) {
            // 2、没获取到，基于DriverManager获取Connection，然后添加到ThreadLocal中
            try {
                conn = DriverManager.getConnection(PROP.getProperty("jdbc.url"), PROP.getProperty("jdbc.username"), PROP.getProperty("jdbc.password"));
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException("获取Connection连接出错!!!");
            }
            tl.set(conn);
        }

        // 3、获取到了，直接返回
        return conn;
    }

    /**
     * 3.针对写操作的释放资源
     *
     * @param conn 释放连接对象
     * @param stmt 释放发送SQL的对象
     */
    public static void closeAll(Connection conn, Statement stmt) {
        try {
            if (stmt != null) {
                stmt.close();
            }
            if (conn != null) {
                conn.close();
                tl.remove();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("关闭连接失败!!!");
        }
    }

    /**
     * 3.针对查询操作的释放资源
     *
     * @param conn 释放连接对象
     * @param stmt 释放发送SQL的对象
     * @param rs   释放查询对象
     */
    public static void closeAll(Connection conn, Statement stmt, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (stmt != null) {
                stmt.close();
            }
            if (conn != null) {
                conn.close();
                tl.remove();
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException("关闭连接失败!!!");
        }
    }

    /**
     * 开启事务
     */
    public static void beginCommit() {
        Connection conn = getConnection();
        try {
            conn.setAutoCommit(false);
        } catch (SQLException e) {
            throw new RuntimeException("开启事务失败!");
        }
    }

    /**
     * 提交事务
     */
    public static void commit() {
        Connection conn = getConnection();
        try {
            conn.commit();
        } catch (SQLException e) {
            throw new RuntimeException("提交事务失败!");
        } finally {
            closeAll(conn, null);
        }
    }

    /**
     * 回滚事务
     */
    public static void rollback() {
        Connection conn = getConnection();
        try {
            conn.rollback();
        } catch (SQLException e) {
            throw new RuntimeException("回滚事务失败!");
        } finally {
            closeAll(conn, null);
        }
    }
}
